from flask import request
from collections import OrderedDict
from ext import db
from base.model_to_iterable import model_to_dict_list
import json

'1、通用分页器'
class GenericPagination:
    query_page = 'page'  #request.args.get('page') 获取查询页码
    query_page_size = 'page_size' #request.args.get('page_size') 获取查询页面大小
    page_size = 5 #默认的每页大小
    max_page_size = 20  #限制用户可以获取的最大每页大小，防止用户输入过大的数值，导致查询操作过大

    def __init__(self,queryset,fields = None,exclude_fields = None):
        '''
        queryset: 要进行分页的数据，可以是模型对象、restful marshal处理后的列表数据
        fields: 指定展示的字段
        exclude_fields: 指定不展示的字段
        '''
        self.queryset = queryset
        self.fields = fields
        self.exclude_fields = exclude_fields

    def get_page(self,all_page):
        '''
        处理前端传递的page值是否合法
        '''
        page = request.args.get(self.query_page)
        if page:
            if page.isdigit():
                page = int(page)
                if page <1:
                    return {'code':400,'error':'页码数必须大于0'}
                if page > all_page:
                    return {'code':400,'error':f'传递的页码数{page}超过最大页码数{all_page}'}

                return int(page)
            else:
                return {'code':400,'error':'页码数必须是数值'}
        else:
            return {'code':400,'error':'请携带上要查询的页码数,page'}

    def get_page_size(self):
        '''
        处理前端传递的page_size值是否合法
        '''
        page_size =  request.args.get(self.query_page_size)
        if not page_size:
            page_size = self.page_size
            return page_size
        else:
            if page_size.isdigit():
                page_size = int(page_size)
                if page_size<1:
                    return {'code':400,'error':'每页大小必须大于0'}
                if page_size>self.max_page_size:
                    return self.max_page_size
                return int(page_size)
            else:
                return {'code':400,'error':'查询的每页大小必须是数值'}
    def handle_fields(self,data,fields,exclude_fields):
        '''
        功能：处理要展示字段，指定要展示字段优先，指定不展示字段
        '''
        keys = list(data[0].keys())
        delete_keys = [] #不想展示的字段
        if fields:
            for key in keys[::-1]:
                if key not in fields:
                    delete_keys.append(key)

        elif exclude_fields:
            for key in keys[::-1]:
                if key in exclude_fields:
                    delete_keys.append(key)
        else:
            return data
        #把不想展示的字段通通删除掉
        for dic in data:
            for key in delete_keys:
                try:
                    dic.pop(key)
                except Exception as _:
                    pass
        return data


    def handle_data(self,data):
        '''
        对响应的页数据进行特殊处理
        '''
        return data

    def start(self):
        '''
        返回查询的结果
        '''

        #1、根据传递进来的不同数据类型进行不同的处理
        error_data = {
            'code': 400,
            'error': '查询不到数据',
        }
        if not self.queryset:
            return error_data
        else:
            if isinstance(self.queryset[0],db.Model):
                #传递进来的数据库查询对象
                all_data = model_to_dict_list(self.queryset)
            elif isinstance(self.queryset[0],OrderedDict):
                #传递进来的是marshal序列化后的结果
                all_data = json.loads(json.dumps(self.queryset))
            elif isinstance(self.queryset[0],dict):
                #传递进来的是[{}] 格式
                all_data = self.queryset
            else:
                raise Exception('分页器内部传递的数据格式有问题')


        #2、前端携带的数据校验
        # 总数据量
        amounts = len(all_data)
        # 校验前端传递的page_size是否正确
        page_size = self.get_page_size()
        if isinstance(page_size, dict):
            return page_size
        # 总页数
        all_page, add_page = divmod(amounts, page_size)
        if add_page:
            all_page +=1
        #校验前端传递的page是否正确
        page = self.get_page(all_page)
        if isinstance(page, dict):
            return page

        # 是否有下一页
        if page>0 and page<all_page and all_page>1:
            next_page = 1
        else:
            next_page = 0

        # 是否有上一页
        if page>1 and page <=all_page and all_page>1:
            pre_page = 1
        else:
            pre_page = 0

        # 返回的数据
        if page==1:
            page_data = all_data[:page*page_size]
        else:
            page_data = all_data[(page-1)*page_size:page*page_size]
        #2.1、处理要展示的字段
        self.handle_fields(data=all_data,fields=self.fields,exclude_fields=self.exclude_fields)
        #2.1、返回的数据进行处理，修改返回的数据内容
        page_data = self.handle_data(page_data)

        # 当前页数据量
        page_amount = len(page_data)

        ret_data = {
            'code':200,
            'amounts':amounts, #总数据量
            'page':page, #查询页码
            'page_size':page_size, #查询页面大小
            'page_amount': page_amount, #当前页返回的数据量
            'all_page':all_page, #总页码数
            'next_page':next_page, #是否有下一页
            'prev_page':pre_page, #是否有上一页
            'data':page_data #当前页的数据
        }
        return ret_data

'2、其他继承自通用的分页器: 需要就响应的数据进一步的修改时使用'
class UserPagination(GenericPagination):

    def handle_data(self,data):
        '''
        在这里对响应的页数据进行操作，修改里面的数据啥的，注意，要return 修改后的数据
        '''
        for dic in data:
            dic['default']='新处理的数据'
        return data



if __name__ == '__main__':
    '''
    使用：
    pagination = GenericPagenation(datas)
    ret = pagination.start()
    '''

